package com.lang.thread.status_transform;

import java.util.concurrent.atomic.AtomicBoolean;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintStream;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 查看线程集中状态转换
 * Exception in thread "main" java.lang.IllegalMonitorStateException  说明nofity和wait都必须写在synchronized里面 要先获取锁
 * 结果信息简化在 ./status线程切换状态输出数据  文件里面
 * 1.启动Monitor线程 死循环  然后启动Runner线程；确保Runner线程stat前的状态被打印
 * 2.主线程sleep(30)
 *   Runner线程sleep 5 然后wait(释放锁) 等待主线程释放lock
 * 3.主线程进入同步块，然后notify Runner线程
 * 4.runner线程和main 一起执行
 * notify和wait之后就释放锁了吗？ wait执行完了后释放锁
 * notify是当前代码块结束了才释放锁吗？ notify代码块结束后才释放锁，notify只是通知线程进入blocked状态，并进入同步队列
 */
public class StatusDemo {
    public static AtomicBoolean monitorStarted = new AtomicBoolean(false);
    public static AtomicBoolean runnerWaited = new AtomicBoolean(false);
    public static void main(String[] args) throws FileNotFoundException, InterruptedException {
        String path = "./temp";
        File file = new File(path);
        PrintStream out = new PrintStream(file);
        System.setOut(out);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd HH:mm:ss:SSS ");

        System.out.println("start " + sdf.format(new Date()));
        Object lock = new Object();
        Runner r = new Runner("runner", lock);
        Monitor m = new Monitor(r);
        Thread tm = new Thread(m);
        tm.start();

        while(!monitorStarted.get()){

        }

        r.start();

        while(!runnerWaited.get()){

        }
        Thread.sleep(5);

        System.out.println("##########before synchronized\t" + r.getId() + "\t" + sdf.format(new Date()) + "\t" + r.getState());

        synchronized (lock) {
            System.out.println("##########mid synchronized\t" + r.getId() + "\t" + sdf.format(new Date()) + "\t" + r.getState());
            lock.notify();
            Thread.sleep(5);
        }
        System.out.println("###############after synchronized\t" + r.getId() + "\t" + sdf.format(new Date()) + "\t" + r.getState());
//        for (StackTraceElement s : r.getStackTrace()) {
//            System.out.println("###############after synchronized" + s);
//        }
        try {
            Thread.sleep(10);//防止main线程关闭了，runner线程没跑完
        }
        catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("end " + sdf.format(new Date()));

        System.exit(0);//如果前面抛出异常的话，这个就不执行，然后Monitor线程就会已知跑下去

    }
}

/**
 * 监控线程
 */
class Monitor implements Runnable {
    Thread thread;

    public Monitor(Thread thread) {
        this.thread = thread;
    }

    @Override
    public void run() {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd HH:mm:ss:SSS ");
        System.out.println(thread.getName() + "\t" + sdf.format(new Date()) + "\t" + thread.getState());
        StatusDemo.monitorStarted.set(true);
        while (true) {
            System.out.println(thread.getName() + "\t" + sdf.format(new Date()) + "\t" + thread.getState());
        }
    }
}

/**
 * 实际运行线程
 */
class Runner extends Thread {
    Object lock;


    public Runner(String name, Object lock) {
        super(name);
        this.lock = lock;
    }

    @Override
    public void run() {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd HH:mm:ss:SSS ");
        System.out.println("---------after start\t" + this.getId() + "\t" + sdf.format(new Date()) + this.getState());

        try {
            Thread.sleep(5);
        }
        catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("--------- before synchronized\t" + this.getId() + "\t" + sdf.format(new Date()) + this.getState());



        synchronized (lock) {
            try {

                try {
                    Thread.sleep(5);
                }
                catch (InterruptedException e) {
                    e.printStackTrace();
                }

                System.out.println("---------after synchronized before wait\t" + this.getId() + "\t" + sdf.format(new Date()) + this.getState());


                StatusDemo.runnerWaited.set(true);
                lock.wait();

                System.out.println("---------after wait before out synchronized\t" + this.getId() + "\t" + sdf.format(new Date()) + this.getState());
            }
            catch (InterruptedException e) {
                e.printStackTrace();
            }

            try {
                Thread.sleep(5);
            }
            catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println("---------after synchronized\t" + this.getId() + "\t" + sdf.format(new Date()) + this.getState());

        }
    }
}